﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Networks;

namespace DiscoveryLogic.Analytics.BusinessLogic.Measures
{
    /// <summary>
    /// Closeness centrality using geodesic distance is the reciprocal of the 
    /// sum of geodesic distances to all other vertices in the graph. the Score is 
    /// normalized against maximum value
    /// 
    /// Another way of thinking about how close an actor A is to all others is to calculate the
    /// proportion of other actors that A can reach in one step, two steps, three steps, etc (or,
    /// alternatively, the proportion of nodes that reach A in n steps). One can then calculate a
    /// single index for each node by summing up the proportion of other nodes reached (for the
    /// first time) at a given distance, appropriately weighted (e.g. 1 for nodes at distance 1, ½
    /// for nodes at distance 2…). These scores can be then normalised dividing by the
    /// maximum value, if this is considered appropriate.
    /// 
    /// Closeness can be regarded as a measure of how long it will take information to 
    /// spread from a given vertex to other reachable vertices in the network
    /// </summary>
    public class ClosenessCentrality : INetworkMeasure
    {
       
        #region Implementation of IMeasure

        /// <summary>
        /// 
        /// </summary>
        public MeasureType MeasureType
        {
            get
            {
                return Measures.MeasureType.Vertice;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string MeasureName
        {
            get
            {
                return "Centrality";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string Algorithm
        {
            get
            {
                return "Freeman";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public string Description
        {
            get
            {
                return
                    @"Closeness centrality using geodesic distance is the reciprocal of the 
    sum of geodesic distances to all other vertices in the graph. the Score is 
    normalized against maximum value";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public Dictionary<Guid, double> GetVerticeMeasure(Graph g)
        {
            Dictionary<Guid, double> centralityMeasures = new Dictionary<Guid, double>();
            foreach (Vertice v in g.Vertices.Values)
            {
                double sumGeodesicDistance = 0;
                // Vertice v = vs[0];
                List<Vertice> vertices = new List<Vertice>();
                vertices.AddRange(g.Vertices.Values);
                List<Edge> edges = new List<Edge>();
                edges.AddRange(g.Edges.Values);
                NetworkWalker walker = new NetworkWalker(vertices, edges);
                Dictionary<Vertice, Route> shortestPaths = walker.GetShortestPathsFrom(v);
                if (shortestPaths != null && shortestPaths.Count > 0)
                {
                    foreach (Vertice vTgt in shortestPaths.Keys)
                    {
                        if (vTgt.ID != v.ID && shortestPaths[vTgt].Distance < double.PositiveInfinity)
                        {
                            sumGeodesicDistance += shortestPaths[vTgt].Distance;
                        }
                    }
                }
                
                double centrality = 0;
                if (sumGeodesicDistance > 0)
                {
                    centrality = ((double)(g.Vertices.Count - 1)) / sumGeodesicDistance;
                }
                centralityMeasures.Add(v.ID, centrality);
            }
            return centralityMeasures;
        }

        #endregion
    }
}
